// 接口
// 行为和动作的【规范】，对批量方法进行【约束】,想想现实中的各种接口
// 和上一讲中的abstract类有类似的地方

// 属性类
// 1. 对json格式严格约束
// old
function print(obj: { name: string; age: number }): void {
  console.log(obj);
}

// test
interface IUserInfo {
  id: number;
  name: string;
}

function log(userInfo: IUserInfo): void {
  console.log(userInfo.id + userInfo.name);
}
log({ id: 1, name: "xf" });
// 【注意】 这样传入参数，需要保证接口中的字段都包含。  不建议这样写，或者对xiaoming同样做接口限制!
const xiaoming = { id: 2, name: "xiaoming", x: 1 };
log(xiaoming);

// now
// 通过接口来接管 【格式】，比如，函数的参数
interface FullName {
  first: string;
  second: string;
}

function printName({ first, second }: FullName): void {
  // console.log(param.first + param.second);
  // 即便使用了传参的形式来调用，在执行时依然会报错，所以建议还是【严格】按照interface的规范来。
  // console.log(first + second + third);
}

// 通过传参的形式调用， 可以传入多个参数。如果直接写的话，就完全按interface来
const fullName = { first: "1", second: "2", third: "3" };

printName(fullName);

// 2. 可选属性 ?:
interface SquareConfig {
  color?: string;
  width?: number;
  // 便于额外参数的传入
  // [propName: string]: any;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  let newSquare = { color: "white", area: 100 };
  // 如果接口中的字段可能为空，那么在函数体内【必须】要对空的情况作判断
  if (config.color) {
    newSquare.color = config.color;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({ color: "black" });

// 3. 只读属性 readonly

// 4. 多余/额外 的参数 (避免校验)
// 4-1 通过断言来强制传入
let yourSquare = createSquare({ length: 123 } as SquareConfig);
// 4-2 修改interface

// 4-3 通过传入一个变量的形式
let config = { color: "red", width: 100, length: 123 };

createSquare(config);

// 函数类型 对传入的参数和返回值进行(公共)约束, 【带了个括号】
// 1. 普通函数类型接口
interface encrypt {
  (key: string, value: string): string;
  //   test?: () => void;
  //   (): void; // :前面就是函数的param，后面就是函数的返回值
}
// 讲道理，确实应该直接在声明的变量后面紧跟: INTERFACE
const md5: encrypt = function(a: string, b: string): string {
  return a + b;
};

md5("1", "2");

// 【可索引】 数组 和 对象 的约束 (不常用)
// 和函数类型的接口有点相似 ()变成了[] 然后在[]中的"参数"就是index 类型就是number
// TS定义数组的方式
// const arr: number[] = [1, 2, 3];
// const arr1: Array<string> = ['1', '2'];
interface UserArr {
  [index: number]: string;
}

const arr: UserArr = ["a", "b"];
console.log(arr[0]);

interface UserObj {
  [key: string]: string;
}
const obj: UserObj = { name: "pal" };

// 类的类型 对类的约束，类似于abstract    【常用】
// test 类的接口实现的时候，在方法中可以不用带参数
interface Student1 {
  id: number;
  name: string;
  shout(words: string): string;
}
// ts 中的 class看起来还有点严格。 连属性都需要单独再声明一次……
class GradeOne implements Student1 {
  // 声明该类的 属性 的类型 ！！！ 前后都要有！！！！
  // 不过即使上面用了implements之后，【仍然不能】省略类内部的这些定义...
  public id: number;
  name: string;
  constructor(id: number, name: string) {
    this.id = id;
    this.name = name;
  }
  // 【注意】 此处可以省略参数，但是这个方法必须要有!
  // 类的方法也需要前后都有声明！！！
  public shout(): string {
    return this.id + this.name;
  }
}
interface Animal1 {
  name: string;
  eat(str: string): void;
}

class Bird implements Animal1 {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }

  public eat(str: string): void {
    console.log(this.name + ", eat", str);
  }
}

const bird1 = new Bird("lucky");

bird1.eat("meant");

// 扩展 接口继承接口 类继承同时满足接口
// interface A extends interface B, C {
//
// }
// 可以有下面这种风骚的操作，但是没必要...
// class C extends D implements A {}

// 居然还有typeof 这种骚操作... ，但是好像只能用于基本类型...
const a: string = "a";
const b: typeof a = "b";

// interface IStringMap {
//   id: number;
//   [key: string]: string;
// }

// const xiafan: IStringMap = {
//   id: 1,
//   name: "xiafan"
// };

// console.log(xiafan.name);

// function f(p: IStringMap): void {
//   console.log(p['id']);
//   console.log(p['idd']);
// }

// interface 还可以继承class，此时可以继承到类上的【私有成员】
class Control {
  private state: any;
}

interface ISelectableControl extends Control {
  select(): void;
}

// 子类 实现上面继承后的接口
class Button extends Control implements ISelectableControl {
  select() {}
}

class TextC extends Control {}

// 这里就报错了。因为ImageC并没有 state 这个私有属性
// class ImageC implements ISelectableControl {
// 即便是这样也是没用的，因为这里的state和上面Control里的state来源不同
// private state: any;
//   select() { }
// }
